
/**
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
let minWindow = function (s, t) {
    let letterMap = new Map(),
        letterCount = 0,
        left = 0,
        right = 0,
        res = "",
        sLen = s.length,
        tLen = t.length

    // 统计t中含有多少个字母，letterMap为各字母出现的次数，letterCount代表字母数
    for (let i = 0; i < tLen; i++) {
        let mapValue = letterMap.get(t[i])
        letterMap.set(t[i], (mapValue || 0) - 1)
        // 字母第一次出现时，++
        if ((mapValue || 0) == 0) { letterCount++ }
    }

    // 从s中初始化map，找到第一个符合的字符串
    for (let i = 0; i < sLen; i++) {
        let mapValue = letterMap.get(s[i])
        if (mapValue !== undefined) {
            // 只要字母出现，就要加一
            letterMap.set(s[i], mapValue + 1)
            // 如果加到0，说明该字母匹配上了，以后为1为2，可以不管
            if (mapValue + 1 == 0) { letterCount-- }
        }
        // 所有字母匹配完成
        if (letterCount === 0) {
            right = i
            res = s.slice(0, i + 1)
            break
        }
    }

    if (res === "") return ""

    // 左侧值边界
    while (left <= sLen - tLen) {
        // 判断左侧滑过的值
        let mapValueLeft = letterMap.get(s[left])
        if (mapValueLeft !== undefined) {
            letterMap.set(s[left], mapValueLeft - 1)
            // 如果值滑过后，小于0，则代表该值不再匹配，否则可能一个字母为1为2，划走一个不耽误
            if (mapValueLeft - 1 < 0) { letterCount++ }
        }
        left++
        // 所有字母匹配完成，判断新串
        if (letterCount === 0) {
            res = (right - left + 1) < res.length ? s.slice(left, right + 1) : res
        }

        // 右侧值边界。如果左侧值滑过后，所有字母还是匹配完成的话，右侧值不动
        while (letterCount !== 0 && right < sLen) {
            right++
            let mapValueRight = letterMap.get(s[right])
            if (mapValueRight !== undefined) {
                letterMap.set(s[right], mapValueRight + 1)
                // 滑进来的值都要加1，加1以后如果为0，则匹配完成
                if (mapValueRight + 1 === 0) { letterCount-- }
            }
        }
    }

    return res
};